package com.xxxtea.component;

import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.xxxtea.common.UserContext;
import com.xxxtea.entity.*;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;

import java.util.HashMap;
import java.util.Map;

/**
 * MyBatis-Plus用户多租户数据隔离处理器
 * 
 * 用于实现用户级别的数据隔离，在SQL查询时自动注入user_id租户条件。
 * 支持指定表和字段的隔离，自动识别@TableName注解和Lambda表达式获取字段名。
 * 仅普通用户角色生效，其他角色不隔离。
 * 
 * @author badman
 * @since 1.0.0
 */
@Slf4j
public class UserTenantLineHandler implements TenantLineHandler {
	/** 当前表名线程变量，用于动态获取隔离字段 */
	private static final ThreadLocal<String> CURRENT_TABLE = ThreadLocal.withInitial(() -> "user_id");
	/** 需要进行数据隔离的表与字段映射 */
	private final Map<String, String> tableMap = new HashMap<>();

	/**
	 * 构造方法，注册需要隔离的表及其userId字段
	 */
	public UserTenantLineHandler() {
		registerField(Cart.class, Cart::getUserId);
		registerField(Order.class, Order::getUserId);
		registerField(Collect.class, Collect::getUserId);
		registerField(OrderItem.class, OrderItem::getUserId);
		registerField(UserAddress.class, UserAddress::getUserId);
		registerField(SearchHistory.class, SearchHistory::getUserId);
		registerField(BrowseHistory.class, BrowseHistory::getUserId);
		log.info("启用数据隔离的表: {}, 隔离字段: [{}]", tableMap, getTenantIdColumn());
	}

	/**
	 * 注册需要隔离的表和字段，优先使用@TableName注解，否则用类名转下划线
	 * @param entityClass 实体类
	 * @param func 获取字段的Lambda表达式
	 */
	private <T> void registerField(Class<T> entityClass, Func1<T, ?> func) {
		String className = entityClass.getSimpleName();
		String fieldName = LambdaUtil.getFieldName(func);

		TableName tableName = entityClass.getAnnotation(TableName.class);
		if (tableName != null && !tableName.value().isEmpty()) {
			tableMap.put(tableName.value(), StrUtil.toUnderlineCase(fieldName));
			return;
		}
		tableMap.put(StrUtil.toUnderlineCase(className), StrUtil.toUnderlineCase(fieldName));
	}

	/**
	 * 获取当前表的租户字段名
	 * @return user_id等
	 */
	@Override
	public String getTenantIdColumn() {
		return tableMap.get(CURRENT_TABLE.get());
	}

	/**
	 * 获取当前租户ID表达式
	 * @return LongValue类型的用户ID
	 */
	@Override
	public Expression getTenantId() {
		return new LongValue(UserContext.getId());
	}

	/**
	 * 判断表是否需要进行数据隔离
	 * 仅普通用户角色且在隔离表集合中才隔离
	 * @param tableName 表名
	 * @return true=忽略隔离，false=进行隔离
	 */
	@Override
	public boolean ignoreTable(String tableName) {
		if (User.Role.USER == UserContext.getRole() && tableMap.containsKey(tableName)) {
			CURRENT_TABLE.set(tableName);
			log.info("准备进行数据隔离的表: [{}], 隔离字段: [{}], 租户值: [{}]", tableName, getTenantIdColumn(), UserContext.getId());
			return false;
		}
		return true;
	}
}